<script setup lang="ts">
import { ref, onMounted } from 'vue'
import G6 from '@antv/g6'
import insertCss from 'insert-css'

// define the CSS with the id of your menu

insertCss(`
  #contextMenu {
    position: absolute;
    list-style-type: none;
    padding: 10px 8px;
    left: -150px;
    background-color: rgba(255, 255, 255, 0.9);
    border: 1px solid #e2e2e2;
    border-radius: 4px;
    font-size: 20px;
    color: #545454;
  }
  #contextMenu li {
    cursor: pointer;
		list-style-type:none;
    list-style: none;
    margin-left: 0px;
    font-size: 16px;
  }
  #contextMenu li:hover {
    color: #aaa;
  }
`)

import Mock from '@/views/mock/day4'
const mountNode = ref()

const lineDash = [4, 2, 1, 2]
const edgeName = 'line-cubic'
const personNode = 'personNode'
const nodeSize = [40, 15] // 节点大小
const maleColor = '#6495ED' // 男性节点颜色
const femaleColor = '#FF69B4' // 女性节点颜色

const customFn = () => {
  // 注册节点和边的样式
  G6.registerNode(personNode, {
    draw(cfg, group) {
      const [width, height] = cfg.size || nodeSize
      const rect = group.addShape('rect', {
        attrs: {
          x: -width / 2,
          y: -height / 2,
          width,
          height,
          stroke: '#fff',
          fill: '#fff'
        }
      })

      // 在节点上方显示头像
      group.addShape('image', {
        attrs: {
          x: -width / 2 + 10,
          y: -height - 10,
          width: 20,
          height: 20,
          img: cfg.avatar
        }
      })

      // 在节点下方显示名字
      if (cfg.tag) {
        group.addShape('text', {
          attrs: {
            x: 0,
            y: height / 2 + 3,
            text: cfg.tag,
            fill: cfg.gender === 'male' ? maleColor : femaleColor,
            fontSize: 7,
            textAlign: 'center',
            writingMode: 'horizontal-tb',
            textBaseline: 'middle'
          }
        })
      }

      group.addShape('text', {
        attrs: {
          x: 0,
          y: height / 2 - 5,
          text: cfg.label,
          fill: cfg.gender === 'male' ? maleColor : femaleColor,
          fontSize: 7,
          textAlign: 'center',
          writingMode: 'horizontal-tb',
          textBaseline: 'middle'
        }
      })

      return rect
    }
  })
  // 自定义边
  G6.registerEdge(
    edgeName,
    {
      afterDraw(cfg: any, group: any) {
        const startPoint = cfg.startPoint
        const endPoint = cfg.endPoint
        group.addShape('text', {
          attrs: {
            x: (startPoint.x + endPoint.x) / 2,
            y: (startPoint.y + endPoint.y) / 2,
            text: cfg.source,
            fill: '#666',
            fontSize: 3,
            textAlign: 'center',
            textBaseline: 'middle'
          },
          name: 'left-text-shape'
        })
      },
      /**
       * 单图形的边仅考虑 selected、active 状态，有其他状态需求的用户自己复写这个方法
       * @param  {String} name 状态名称
       * @param  {Object} value 状态值
       * @param  {Edge} edge 边
       */
      setState(name, value, edge: any) {
        const shape = edge.get('keyShape')
        if (name === 'active') {
          if (value) {
            let index = 0
            shape.animate(
              () => {
                index++
                if (index > 9) {
                  index = 0
                }
                const res = {
                  lineDash,
                  lineDashOffset: -index
                }
                // return the params for this frame
                return res
              },
              {
                repeat: true,
                duration: 3000
              }
            )
          } else {
            shape.stopAnimate()
            shape.attr('lineDash', null)
          }
        }
      }
    },
    'cubic-vertical' // 被继承的边类型  cubic-horizontal
  )
}
customFn()

const contextMenu = new G6.Menu({
  getContent(evt: any) {
    return `
    <h3>添加子节点</h3>
    <ul>
      <li title='1'>子节点->1</li>
      <li title='2'>子节点->2</li>
    </ul>`
  },
  handleMenuClick: (target, item) => {
    alert(JSON.stringify(target))
  },
  // offsetX and offsetY include the padding of the parent container
  // 需要加上父级容器的 padding-left 16 与自身偏移量 10
  offsetX: 16 + 10,
  // 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
  offsetY: 0,
  // the types of items that allow the menu show up
  // 在哪些类型的元素上响应
  itemTypes: ['node']
})

const init = () => {
  // 定义数据、实例化图、加载数据、渲染图
  const graph = new G6.Graph({
    container: mountNode.value,
    width: document.documentElement.clientWidth * 0.6,
    height: document.documentElement.clientHeight * 0.7,
    animate: true, // 启用动画
    fitView: true, // 自动适配画布大小
    plugins: [contextMenu],
    modes: {
      default: ['drag-canvas', 'drag-node', 'zoom-canvas', 'activate-relations']
    },
    defaultNode: {
      type: personNode
    },
    layout: {
      type: 'dagre',
      nodeSize,
      rankdir: 'TB', // 布局的方向 从左至右布局 LR, TB, RL, BT
      // align: '', // 节点对齐方式 对齐到左上角 UL, UR, DL, DR  默认，中间对齐
      nodesepFunc: () => 1, // 节点的水平间距
      ranksepFunc: () => 1 // 竖直方向相邻层间距 层之间的间距
    },
    defaultEdge: {
      type: edgeName,
      style: {
        lineWidth: 0.5,
        stroke: 'rgb(51, 136, 255)',
        endArrow: {
          // svg path语法 M表示移动到指定的位置，L表示画一条直线到指定的位置，Z表示闭合路径
          path: 'M 0,0 L 2,1 L 2,-1 Z',
          fill: 'rgb(51, 136, 255)'
        }
      }
    }
  })
  // activate-relations 相关时机事件
  graph.on('afteractivaterelations', (e: { item: any; action: string }) => {
    const { item, action } = e
    // 当前操作的节点 item
    if (action === 'activate') {
      graph.setItemState(e.item, 'hover', true)
    } else {
      graph.setItemState(item, 'hover', false)
    }
  })

  // 设置节点之间的间距
  graph.on('afterlayout', () => {
    const nodes = graph.getNodes()

    const elementID = []
    for (let i = 0; i < nodes.length - 1; i++) {
      const { generation } = nodes[i].getModel()
      if (generation === 4) {
        elementID.push(nodes[i])
      }
    }
    //  按照节点的 order 属性进行排序
    elementID.sort((a: any, b: any) => a.getModel()?.order - b.getModel()?.order)

    const width = 110 // 节点的宽度

    elementID.forEach((node, i) => {
      const x = width * i
      graph.updateItem(node, {
        x,
        y: node.getModel().y
      })
    })
    // node4 2级子节点
    const nodeId = 'node4'
    const nodeIdwife = 'node5'
    const nodeIdTwowife = 'node6'
    const nodeIdBorth = 'node9'
    const nodeIdSelf = 'node12'
    const nodeId16Self = 'node16'
    const nodeId17Self = 'node17'
    const nodeId18Self = 'node18'
    const nodeId19Self = 'node19'
    const nodeId20Self = 'node20'
    const nodeFind = graph.findById(nodeId).getBBox()
    const nodeBrothFind = graph.findById(nodeIdBorth).getBBox()
    const nodeSelfFind = graph.findById(nodeIdSelf).getBBox()

    for (let i = 0; i < nodes.length; i++) {
      const node1 = nodes[i] as any
      const nodeModel = node1.getModel() as any
      const next = nodes[i + 1] as any
      const nextModel = nodes[i + 1]?.getModel() as any
      if (nodeModel?.merge === nextModel?.id && nodeModel.generation === 1) {
        graph.updateItem(node1, {
          x: node1.getModel().x - 62,
          y: node1.getModel().y
        })
        graph.updateItem(next, {
          x: next.getModel().x + 62,
          y: next.getModel().y
        })
      }
      if (nodeModel.merge === nodeId && nodeModel.generation === 2) {
        if (nodeModel.id === nodeIdwife) {
          graph.updateItem(node1, {
            x: nodeFind.x + 50,
            y: node1.getModel().y
          })
        }
        if (nodeModel.id === nodeIdTwowife) {
          graph.updateItem(node1, {
            x: nodeFind.x + 80,
            y: node1.getModel().y
          })
        }
      }
      if (nodeModel?.merge === nextModel?.id && nodeModel.generation === 4) {
        graph.updateItem(next, {
          x: nodeSelfFind.x + 50,
          y: next.getModel().y
        })
      }
      if (nodeModel.id === 'node14' && nodeModel.generation === 4) {
        graph.updateItem(node1, {
          x: nodeBrothFind.x + 20,
          y: node1.getModel().y
        })
      }
      if (nodeModel.generation === 5) {
        graph.updateItem(node1, {
          x: nodeSelfFind.x + 20,
          y: node1.getModel().y
        })
      }
      if (nodeModel?.num) {
        if (nodeModel.id === nodeId16Self) {
          const { x, y } = graph.findById('node2').getBBox()
          graph.updateItem(node1, {
            x: x - 149,
            y: y - 10
          })
        }
        if (nodeModel.id === nodeId17Self) {
          const { x, y } = graph.findById('node3').getBBox()
          graph.updateItem(node1, {
            x: x - 95,
            y: y - 10
          })
        }
        if (nodeModel.id === nodeId18Self) {
          const { x, y } = graph.findById('node7').getBBox()
          graph.updateItem(node1, {
            x: x - 50,
            y: y - 10
          })
        }
        if (nodeModel.id === nodeId19Self) {
          const { x, y } = graph.findById('node11').getBBox()
          graph.updateItem(node1, {
            x: x - 30,
            y: y - 10
          })
        }
        if (nodeModel.id === nodeId20Self) {
          const { x, y } = graph.findById('node15').getBBox()
          graph.updateItem(node1, {
            x: x - 140,
            y: y - 20
          })
        }
      }
    }
  })
  // @ts-ignore
  graph.data(Mock) //  加载数据
  graph.render() // 渲染图

  graph.on('node:click', (evt) => {
    const { item } = evt
    alert(item.getModel().label)
  })
}

onMounted(() => {
  init()
})
</script>
<template>
  <div ref="mountNode" class="container" />
</template>

<style lang="scss" scoped>
.container {
  border: 4px solid #5b8ff9;
}
</style>
